package ldk.xposed.dexreplace.utils

import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.*

/**
 * Created by liangdengke on 2018/4/23.
 */
class ReflectUtil {
    companion object {
        fun findField(clazz: Class<*>, fieldName: String): Field{
            var current = clazz
            while (current != Any::class.java){
                try {
                    val field = current.getDeclaredField(fieldName)
                    if (!field.isAccessible){
                        field.isAccessible = true
                    }
                    return field
                }catch (e: NoSuchFieldException){
                    current = (current.superclass as Class<out Any>?)!!
                }
            }
            throw NoSuchFieldException("$fieldName not found in $clazz and its ancestor")
        }

        fun expandFieldArray(instance: Any,
                             arrayFiledName: String,
                             extraElement: Array<Any>){
            val field = findField(instance::class.java, arrayFiledName)
            val originValue = field.get(instance) as Array<Any>
            val componentType = originValue::class.java.componentType
            val combineValue = java.lang.reflect.Array.newInstance(componentType, originValue.size + extraElement.size) as Array<Any>
            System.arraycopy(extraElement, 0, combineValue, 0, extraElement.size)
            System.arraycopy(originValue, 0, combineValue, extraElement.size, originValue.size)
            field.set(instance, combineValue)
        }

        fun findMethod(instance: Any, methodName: String, vararg args: Class<*>): Method{
            var current = instance::class.java
            while (current != Any::class.java){
                try {
                    val method = current.getDeclaredMethod(methodName, *args)
                    if (!method.isAccessible){
                        method.isAccessible = true
                    }
                    return method
                }catch (e: NoSuchMethodException){
                    current = (current.superclass as Class<out Any>?)!!
                }
            }

            throw NoSuchMethodException("$methodName: $args method not found")
        }

        /**
         * Locates a given constructor anywhere in the class inheritance hierarchy.
         *
         * @param instance       an object to search the constructor into.
         * @param parameterTypes constructor parameter types
         * @return a constructor object
         * @throws NoSuchMethodException if the constructor cannot be located
         */
        @Throws(NoSuchMethodException::class)
        fun findConstructor(instance: Any, vararg parameterTypes: Class<*>): Constructor<*> {
            var clazz: Class<*>? = instance.javaClass
            while (clazz != null) {
                try {
                    val ctor = clazz.getDeclaredConstructor(*parameterTypes)

                    if (!ctor.isAccessible) {
                        ctor.isAccessible = true
                    }

                    return ctor
                } catch (e: NoSuchMethodException) {
                    // ignore and search next
                }

                clazz = clazz.superclass
            }

            throw NoSuchMethodException("Constructor"
                    + " with parameters "
                    + Arrays.asList(*parameterTypes)
                    + " not found in " + instance.javaClass)
        }
    }
}
